package com.terra.ns.imp.auth.service

import com.terra.ns.imp.auth.config.UserLoginProperties
import com.terra.ns.imp.auth.constant.AuthErrorCode
import com.terra.ns.imp.auth.constant.enums.AuthSmsTemplateTypeEnum
import com.terra.ns.imp.auth.exception.AuthServiceException
import com.terra.ns.imp.auth.vo.request.LoginRequest
import com.terra.ns.imp.auth.vo.response.UserLoginResponse
import com.terra.ns.imp.common.redis.utils.RedisUtils
import com.terra.ns.imp.common.satoken.utils.LoginUtils
import com.terra.ns.imp.system.api.entity.LoginUser
import com.terra.ns.imp.system.api.entity.StUser
import com.terra.ns.imp.system.api.enums.UserStatusEnum
import com.terra.ns.imp.system.api.service.ISmsCaptchaApi
import com.terra.ns.imp.system.api.service.IUserApi
import com.terra.ns.imp.system.api.vo.request.SmsCaptchaRequest
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

/**
@author qins
@date 2023/6/2
@desc
 */
@Service
class LoginServiceImpl : ILoginService {

    private lateinit var userApiService: IUserApi

    private lateinit var smsCaptchaApi: ISmsCaptchaApi

    @Autowired
    private lateinit var userLoginProperties: UserLoginProperties

    override fun login(request: LoginRequest): UserLoginResponse {
        // 检查登录参数
        checkLoginParam(request)
        // 检查用户
        val user = userApiService.getUserByPhone(request.phone)
            ?: throw AuthServiceException(AuthErrorCode.USER_NOT_EXIST)
        // 检查登录密码
        checkPassword(request.phone, request.password, user.password)
        // 校验短信验证码
        checkLoginSmsCaptcha(request)
        return doLogin(user)
    }


    override fun passwordlessLogin(phone: String) : UserLoginResponse {
        // 检查用户
        val user = userApiService.getUserByPhone(phone)
            ?: throw AuthServiceException(AuthErrorCode.USER_NOT_EXIST)
        return doLogin(user)
    }

    private fun doLogin(user : StUser) : UserLoginResponse {
        val userPhone = user.phone!!
        val userCode = user.code!!
        // 检查是否被锁
        checkRedisLocked(userPhone)
        // 检测账号状态
        checkAccountStatus(user.status)
        val loginToken = LoginUtils.login(LoginUser(userCode, userPhone))
        return UserLoginResponse(loginToken)
    }


    override fun getLoginSmsCode(request: SmsCaptchaRequest): String {
        request.templateType = AuthSmsTemplateTypeEnum.LOGIN.type
        return smsCaptchaApi.getPhoneCaptcha(request)
    }

    private fun checkAccountStatus(status: Int?) {
        when (status) {
            UserStatusEnum.NORMAL.code -> return
            UserStatusEnum.LOCKED.code -> throw AuthServiceException(AuthErrorCode.USER_ACCOUNT_LOCKED)
            UserStatusEnum.DISABLE.code -> throw AuthServiceException(AuthErrorCode.USER_ACCOUNT_DISABLE)
            UserStatusEnum.EXCEPTION.code -> throw AuthServiceException(AuthErrorCode.USER_ACCOUNT_EXCEPTION)
            UserStatusEnum.LOGOUT.code -> throw AuthServiceException(AuthErrorCode.USER_ACCOUNT_LOGOUT)
            else -> throw AuthServiceException(AuthErrorCode.USER_ACCOUNT_EXCEPTION)
        }
    }

    private fun checkRedisLocked(phone: String) {
        if(RedisUtils.hasKey(LoginUtils.getLockUserKey(phone))) {
            throw AuthServiceException(AuthErrorCode.USER_ACCOUNT_LOCKED)
        }
    }

    private fun checkLoginParam(request: LoginRequest) {
        if (request.smsCodeUuid.isNullOrBlank() && request.password.isBlank()) {
            throw AuthServiceException(AuthErrorCode.LOGIN_VERIFY_PARAM_ERROR)
        }
    }

    private fun checkLoginSmsCaptcha(request: LoginRequest) {
        if(!smsCaptchaApi.verifySmsCaptcha(request.smsCodeUuid, request.phone, AuthSmsTemplateTypeEnum.LOGIN.type, request.smsCode)) {
            throw AuthServiceException(AuthErrorCode.LOGIN_SMS_CAPTCHA_ERROR)
        }
    }

    private fun checkPassword(phone: String, reqPwd: String, dbPwd: String?) {
        if(reqPwd.isBlank()) {
            return
        }
        if (reqPwd != dbPwd) {
            val errorCount = LoginUtils.getPasswordErrorCount(phone)
            if(errorCount >= userLoginProperties.maxRetryCount) {
                LoginUtils.lockLoginUser(phone, userLoginProperties.lockTime)
                throw AuthServiceException(AuthErrorCode.USER_ACCOUNT_LOCKED)
            }
            throw AuthServiceException(AuthErrorCode.USER_LOGIN_PASSWORD_ERROR,
                userLoginProperties.maxRetryCount.toString(),
                (userLoginProperties.lockTime / 60).toString())
        }
    }
}